package com.mrogrape.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 命令行命令编译器。
 * 
 * <p>
 * 构建命令时使用占位符来填充命令参数，占位符的格式为"{paramName}"（不包含双引号），paramName即为参数名，可以通过
 * {@link #setParameter(String, Object)}来设置其值。然后在执行（通过{@link #excute()}方法）时，此参数的占位符将被设置的值填充。
 * 
 * <p>
 * 如果某个参数没有手动指定值，默认情况下在执行时，此参数的占位符将从命令中移除，可以通过{@link #setRetain(boolean)}来更改这个
 * 策略来使执行时保留没有设置值的参数的占位符。
 * 
 * @author Ice Spring
 *
 */
public class CommandBuilder {

	/** 匹配参数占位符用的正则表达式 */
	private static final String REG_EX = "['{'][^'{']+?['}']";

	/**
	 * 创建一个命令行构建器。
	 * 
	 * @param command 命令字符串
	 */
	public CommandBuilder(String command){

		this(command, null);
	}

	/**
	 * 创建一个命令行构建器，并指定参数表。
	 * 
	 * @param command 命令字符串
	 * @param params 参数表列
	 */
	public CommandBuilder(String command, Map<String, Object> params){

		if(command == null || command.length() == 0){

			throw new IllegalArgumentException("Command cannot be empty.");
		}

		this.command = command;
		this.analysisCommand();

		if(params != null){

			this.paramMap.putAll(params);
		}
	}

	/**
	 * 执行构建的命令。
	 * 
	 * <p>
	 * 此方法通过Runtime.getRuntime().exec(String)来执行命令。
	 * 
	 * @return 执行该命令对应的Process对象。
	 * @throws IOException 如果执行命令时发生IO异常。
	 */
	public Process excute() throws IOException{

		String command = this.command;

		for(String param : this.params){

			Object valueObj = this.paramMap.get(param);
			String value = "";
			if(valueObj == null){

				if(this.isRetain){
					continue;
				}
			}
			else {
				value = valueObj.toString();
			}

			String srcStr = "{" + param + "}";
			command = command.replace(srcStr, value);
		}

		return this.runtime.exec(command);
	}

	/**
	 * 设置参数的值。
	 * 
	 *  <p>
	 * 如果设置的参数不存在，则方法会抛出IllegalArgumentException异常。
	 * 
	 * @param name 参数名
	 * @param value 参数值
	 */
	public void setParameter(String name, Object value){

		if(!this.params.contains(name)){
			throw new IllegalArgumentException("Can not find param : '" + name + "' in command line.");
		}

		this.paramMap.put(name, value);
	}

	/**
	 * 获取已经设定的参数值。
	 * 
	 * <p>
	 * 如果获取的参数不存在，则方法会抛出IllegalArgumentException异常。
	 * 
	 * @param name 参数名
	 * @return 已经设置的参数值，如果没有设置返回null.
	 */
	public Object getParameter(String name){

		if(!this.params.contains(name)){
			throw new IllegalArgumentException("Can not find param : '" + name + "' in command line.");
		}

		return this.paramMap.get(name);
	}

	/**
	 * 移除一个已经设定的参数值，如果从来没有设定过这个参数，那么不执行任何操作。
	 * 
	 * @param name 需要移除参数值的参数名。
	 */
	public void removeParameter(String name){

		this.paramMap.remove(name);
	}

	/**
	 * 清除所有已经设定的参数值。
	 */
	public void clear(){

		this.paramMap.clear();
	}

	/**
	 * 查看执行时是否保留未设定参数值的参数的占位符。
	 * 
	 * @return 是否保留未设定参数值的参数的占位符的标记，TRUE的场合保留。
	 */
	public boolean isRetain() {

		return this.isRetain;
	}

	/**
	 * 设置执行时是否保留未设定参数值的参数的占位符，默认情况下不保留。
	 * 
	 * @param isRetain 是否保留未设定参数值的参数的占位符的标记，TRUE的场合保留。
	 */
	public void setRetain(boolean isRetain) {

		this.isRetain = isRetain;
	}

	/**
	 * 分析命令
	 */
	private void analysisCommand(){

		Pattern pattern = Pattern.compile(CommandBuilder.REG_EX);
		Matcher matcher = pattern.matcher(this.command);

		while(matcher.find()){

			String param = this.command.substring(matcher.start() + 1, matcher.end() - 1);
			this.params.add(param);
		}

	}

	/** Runtime */
	private Runtime runtime = Runtime.getRuntime();

	/** 命令字符串 */
	private final String command;

	/** 参数表 */ 
	private final Map<String, Object> paramMap = new HashMap<String, Object>();

	/** 参数 */
	private final List<String> params = new ArrayList<String>();

	/** 标记：是否保留参数 */
	private boolean isRetain = false;
}
